package character;

import java.util.List;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.JFileChooser;
/**
 * 
 * @author justin, matt, will
 * 
 * reads an input file and puts the legal characters into a CharSpec list
 *
 */
public class Parser {
	
	private BufferedReader reader;
	private File inputFile;
	private List<CharSpec> specList;
	private final int MARKLENGTH = 200; 	//the size used for determining how long until the reader.mark() is forgotten.
	/**
	 * 
	 */
	public Parser() {
		specList = new ArrayList<CharSpec>();
	}
	public List<CharSpec> getSpecList(){
		return specList;
	}
	public static String removeChar(String s, char c) {
		StringBuffer sb = new StringBuffer(s.length());
		sb.setLength(s.length());
		int index = 0;
		for (char input : s.toCharArray()) {
			if (input != c) {
				sb.setCharAt(index++, input);
			}
		}
		return sb.toString();
	}
	
	/**
	 * prompts the user to choose a file to be parsed.
	 * adds the file and sets up the required reader.
	 * 
	 * @throws FileNotFoundException
	 * @throws IllegalStateException
	 */
	public void chooseFile() throws FileNotFoundException, IllegalStateException{
		JFileChooser fileChooser = new JFileChooser();
		int returnVal = fileChooser.showOpenDialog(null);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			File inputFile = fileChooser.getSelectedFile();
			BufferedReader reader = new BufferedReader(new FileReader(inputFile));
			this.inputFile = inputFile;
			this.reader = reader;
		} else {
			throw new IllegalStateException();
		}
	}
	
	/**
	 * Reads a predetermined file.
	 * For each line in the file, it throws the parsed version of the
	 * line into a specList
	 */
	public void readFile(){
		try {
			reader.mark(MARKLENGTH);
			while(reader.readLine() != null){
				reader.reset();
				//specList.add(parseLine());
				reader.mark(MARKLENGTH);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
	}
	/**
	 * Parses a line in the input file and returns a character specification.
	 * 
	 * @return a CharSpec for the line
	 * @throws IOException
	 */
	public void parseLine(String line) throws IllegalStateException {
		CharSpec inheritSpec = null;
		CharSpec newSpec;
		boolean isInherited = false;
		if (line != null) {
			line = line.trim();
			int firstBracketpos = line.indexOf('[');		//Beginning of a Regex description
			int secondBracketpos = line.lastIndexOf(']');	//End of a regex description
			String title = line.substring(0, firstBracketpos);
			String regEx = line.substring(firstBracketpos, secondBracketpos + 1);
			if (title.length() + regEx.length() != line.length()) {
				int inheritpos = line.lastIndexOf(" IN ");
				inheritpos = inheritpos + 4;
				String inheritClass = line.substring(inheritpos, line.length());
				inheritClass = inheritClass.trim();
				for (CharSpec spec : specList) {
					if (spec.getTitle().equals(inheritClass)) {
						inheritSpec = spec;
						break;
					}
				}
				if (inheritSpec == null) {
					throw new IllegalStateException("ERROR: Parsing character classes encountered a reference to an undefined class");
				}
				isInherited = true;
			}
			
			title = title.trim();
			newSpec = new CharSpec(title);
			regEx = regEx.trim();
			regEx = regEx.substring(1, regEx.length() - 1);
			String acceptableCharacters = parseRegEx(regEx);
			for (char c : acceptableCharacters.toCharArray()) {
				newSpec.addChar(c);
			}
			
			if (isInherited) {
				for (int i = 0; i < newSpec.getCharList().size(); i++) {
					char k = newSpec.getCharList().get(i);
					boolean isMatch = false;
					for (int j = 0; j < inheritSpec.getCharList().size(); j++) {
						char c = inheritSpec.getCharList().get(j);
						if (c == k) {
							isMatch = true;
						}
					}
					if (!isMatch) {
						newSpec.getCharList().remove(newSpec.getCharList().indexOf(k));
						i--;
					}
				}
			}
			specList.add(newSpec);
		} else {
			

		}
		
	}
	/**
	 * This method parses a regular expression string by expanding it, 
	 * determining if it is inclusive or exclusive, recognizing escape characters,
	 * and returns a string of acceptable characters.
	 * 
	 * @param regEx
	 * @return String
	 */
	private String parseRegEx(String regEx) {
		String acceptableCharacters = "";
		char previousChar = '\0';
		boolean isExclusive = false;
		if (regEx.charAt(0) == '^') {
			isExclusive = true;
			regEx = regEx.substring(1, regEx.length());
		}
		for (int i = 0; i < regEx.length(); i++) {
			char input = regEx.charAt(i);
			if (input == '-') {
				if (previousChar == '\\') {
					acceptableCharacters += input;
					previousChar = '\0';
					continue;
				}
				if((int) previousChar > (int) regEx.charAt(i + 1)) {
					//This is bad - Throw an exception?
				}
				for (int j = (int) previousChar+1; j < (int) regEx.charAt(i + 1); j++) {
					acceptableCharacters += (char) j;
				}
				previousChar = input;
				continue;
			} else if (input == '.') {
				if (previousChar == '\\') {
					acceptableCharacters += input;
					previousChar = '\0';
					continue;
				}
				for (int j = 32; j < 127; j++) {
					acceptableCharacters += (char) j;
				}
				previousChar = input;
				continue;
			} else if (input == '\\') {
				if (previousChar == '\\') {
					acceptableCharacters += input;
					previousChar = '\0';
					continue;
				}
				previousChar = input;
				continue;
			} else if (input == '[') {
				if (previousChar == '\\') {
					acceptableCharacters += input;
					previousChar = '\0';
					continue;
				}
				
			} else if (input == ']') {
				if (previousChar == '\\') {
					acceptableCharacters += input;
					previousChar = '\0';
					continue;
				}
						
			}
			acceptableCharacters += input;
			previousChar = input;
				
			
		}
		if (isExclusive) {
			String allCharacters = "";
			for (int i = 32; i < 127; i++) {
				allCharacters += (char) i;
			}
			for (char c : acceptableCharacters.toCharArray()) {
				for (char k : allCharacters.toCharArray()) {
					if (c == k) {
						allCharacters = removeChar(allCharacters, k);
					}
				}
			}
			acceptableCharacters = "";
			for (char c : allCharacters.toCharArray()) {
					acceptableCharacters += c;
			}
		}
		return acceptableCharacters;
	}
}
